package com.gitee.wsl.mathematics.vector.vec3

import com.gitee.wsl.ext.array.FloatArray
import com.gitee.wsl.io.dataproxy.DataProxy
import com.gitee.wsl.io.dataproxy.asProxy
import com.gitee.wsl.mathematics.coordinate.d3.MutableCoordinate3F
import com.gitee.wsl.mathematics.vector.ext.dot
import com.gitee.wsl.mathematics.vector.ext.times
import com.gitee.wsl.mathematics.vector.vec2.MutableVec2f
import com.gitee.wsl.mathematics.vector.vec4.MutableVec4f
import kotlin.math.*

/**
 * A class representing a three-dimensional / spatial vector.
 */
 class MutableVec3f(override val proxy: DataProxy<Float>):
    Vec3f(proxy=proxy),MutableCoordinate3F<Vec3f> {
    constructor(x: Float=0.0f,
                    y: Float=0.0f,
                    z: Float=0.0f):this(FloatArray(x,y,z).asProxy())
    constructor(a: Number) : this(a.toFloat(), a.toFloat(), a.toFloat())
    constructor(x: Number, y: Number, z: Number) : this(x.toFloat(), y.toFloat(), z.toFloat())
    constructor(base: Vec3f) : this(base.x, base.y, base.z)
    constructor(base: Vector3<*,*>) : this(base.x, base.y, base.z)

    override val self
        get() = this

    //override val vec2 get() = MutableVec2f(x, y)
    //override val vec3 get() = this
    override val vec4 get() = MutableVec4f(x, y, z, 0.0)

    override fun createVec2(a: Float, b: Float): MutableVec2f =MutableVec2f(a,b)

    override fun create(x: Float, y: Float, z: Float): MutableVec3f = MutableVec3f(x, y, z)

    companion object {
        val one get() = MutableVec3f(1.0, 1.0, 1.0)
        val zero get() = MutableVec3f(0.0, 0.0, 0.0)
        val right get() = MutableVec3f(1.0, 0.0, 0.0)
        val left get() = MutableVec3f(-1.0, 0.0, 0.0)
        val up get() = MutableVec3f(0.0, 1.0, 0.0)
        val down get() = MutableVec3f(0.0, -1.0, 0.0)
        val forward get() = MutableVec3f(0.0, 0.0, 1.0)
        val backward get() = MutableVec3f(0.0, 0.0, -1.0)
        val negativeInfinity get() = MutableVec3f(Float.NEGATIVE_INFINITY)
        val positiveInfinity get() = MutableVec3f(Float.POSITIVE_INFINITY)

        val UNIT_X = MutableVec3f(1f, 0f, 0f)
        val UNIT_Y = MutableVec3f(0f, 1f, 0f)
        val UNIT_Z = MutableVec3f(0f, 0f, 1f)
    }

    override fun equals(other: Any?) = other is MutableVec3f && x == other.x && y == other.y && z == other.z
    override fun hashCode(): Int {
        var result = super.hashCode()
        result = 31 * result + x.hashCode()
        result = 31 * result + y.hashCode()
        result = 31 * result + z.hashCode()
        return result
    }

}

/**
 * Parses a 3D Coordinate from a string.
 * @param point String
 * @return 3D Coordinate
 */
fun MutableVec3f.Companion.fromString(point: String): MutableVec3f {
    val point0 = point.trim { it <= ' ' }.replace("[\\[\\]\\s]".toRegex(), "")
    val parts = point0.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
    if (parts.size != 3) throw IllegalArgumentException("Invalid Coordinate Length")

    val x = parts[0].toFloat(); val y = parts[1].toFloat(); val z = parts[2].toFloat()
    return MutableVec3f(x, y, z)
}




fun MutableVec3f.Companion.refract(uv: MutableVec3f, n: MutableVec3f, etaiOverEtat: Float): Vec3f {
    val cosTheta = minOf(-uv.dot(n), 1.0f)
    val rOutPerp = etaiOverEtat * (uv + cosTheta * n)
    val rOutParallel = -sqrt(abs(1.0 - rOutPerp.squaredLength)) * n

    // TODO: なぜか想定通りの値が算出されておらず一時的に「-」にしてある
    return rOutPerp - rOutParallel
}

